package cn.iocoder.yudao.module.promotion.coupon.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.business.basic.dao.query.CoreLambdaQueryWrapper;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.api.product.category.ProductCategoryApi;
import cn.iocoder.yudao.module.api.product.spu.ProductSpuApi;
import cn.iocoder.yudao.module.api.promotion.activity.enums.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.api.promotion.coupon.enums.CouponTakeTypeEnum;
import cn.iocoder.yudao.module.api.promotion.coupon.enums.CouponTemplateStatusEnum;
import cn.iocoder.yudao.module.api.promotion.coupon.enums.CouponTemplateValidityTypeEnum;
import cn.iocoder.yudao.module.promotion.coupon.convert.CouponTemplateConvert;
import cn.iocoder.yudao.module.promotion.coupon.dal.dataobject.CouponTemplateDO;
import cn.iocoder.yudao.module.promotion.coupon.dal.mysql.CouponTemplateMapper;
import cn.iocoder.yudao.module.promotion.coupon.vo.CouponTemplateCreateReqVO;
import cn.iocoder.yudao.module.promotion.coupon.vo.CouponTemplatePageReqVO;
import cn.iocoder.yudao.module.promotion.coupon.vo.CouponTemplateUpdateReqVO;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserLastShopId;
import static cn.iocoder.yudao.module.api.promotion.ErrorCodeConstants.*;

/**
 * 优惠劵模板 Service 实现类
 *
 * @author 芋道源码
 */
@Slf4j
@Service
@Validated
public class CouponTemplateServiceImpl implements CouponTemplateService {

    @Resource
    private CouponTemplateMapper couponTemplateMapper;

    @Resource
    private ProductCategoryApi productCategoryApi;
    @Resource
    private ProductSpuApi productSpuApi;

    @Override
    public Long createCouponTemplate(CouponTemplateCreateReqVO createReqVO) {
        // 校验产品范围
        validateProductScope(createReqVO.getProductScope(), createReqVO.getProductScopeValues());
        // 插入
        CouponTemplateDO couponTemplate = CouponTemplateConvert.INSTANCE.convert(createReqVO);
        couponTemplate.setStatus(CommonStatusEnum.ENABLE.getStatus());
        couponTemplate.setShopId(getLoginUserLastShopId());
        couponTemplateMapper.insert(couponTemplate);
        // 返回
        return couponTemplate.getId();
    }

    @Override
    public void updateCouponTemplate(CouponTemplateUpdateReqVO updateReqVO) {
        // 校验存在
        CouponTemplateDO couponTemplate = validateCouponTemplateExists(updateReqVO.getId());
        // 校验发放数量不能过小（仅在 CouponTakeTypeEnum.USER 用户领取时）
        if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeType())
            && ObjUtil.notEqual(couponTemplate.getTakeLimitCount(), CouponTemplateDO.TAKE_LIMIT_COUNT_MAX) // 非不限制
            && updateReqVO.getTotalCount() < couponTemplate.getTakeCount()) {
            throw exception(COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL, couponTemplate.getTakeCount());
        }
        // 校验产品范围
        validateProductScope(updateReqVO.getProductScope(), updateReqVO.getProductScopeValues());

        // 更新
        CouponTemplateDO updateObj = CouponTemplateConvert.INSTANCE.convert(updateReqVO);
        couponTemplateMapper.updateById(updateObj);
    }

    @Override
    public void updateCouponTemplateStatus(Long id, Integer status) {
        // 校验存在
        validateCouponTemplateExists(id);
        // 更新
        CouponTemplateDO updateObj = new CouponTemplateDO();
        updateObj.setId(id);
        updateObj.setStatus(status);
        couponTemplateMapper.updateById(updateObj);
    }

    @Override
    public void deleteCouponTemplate(Long id) {
        // 校验存在
        validateCouponTemplateExists(id);
        // 删除
        couponTemplateMapper.deleteById(id);
    }

    private CouponTemplateDO validateCouponTemplateExists(Long id) {
        CouponTemplateDO couponTemplate = couponTemplateMapper.selectById(id);
        if (couponTemplate == null) {
            throw exception(COUPON_TEMPLATE_NOT_EXISTS);
        }
        return couponTemplate;
    }

    private void validateProductScope(Integer productScope, Set<Long> productScopeValues) {
        if (Objects.equals(PromotionProductScopeEnum.SPU.getScope(), productScope)) {
            productSpuApi.validateSpuList(productScopeValues);
        } else if (Objects.equals(PromotionProductScopeEnum.CATEGORY.getScope(), productScope)) {
            productCategoryApi.validateCategoryList(productScopeValues);
        }
    }

    @Override
    public CouponTemplateDO getCouponTemplate(Long id) {
        return couponTemplateMapper.selectById(id);
    }

    @Override
    public PageResult<CouponTemplateDO> getCouponTemplatePage(CouponTemplatePageReqVO pageReqVO) {
        return couponTemplateMapper.selectPage(pageReqVO);
    }

    @Override
    public void updateCouponTemplateTakeCount(Long id, int incrCount) {
        int updateCount = couponTemplateMapper.updateTakeCount(id, incrCount);
        if (updateCount == 0) {
            throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
        }
    }

    @Nonnull
    @Override
    public List<CouponTemplateDO> getEnabledCouponTemplateListByTakeType(CouponTakeTypeEnum takeType) {
        List<CouponTemplateDO> list = couponTemplateMapper.selectListByTakeType(takeType.getType(), CommonStatusEnum.ENABLE.getStatus());
        if (CollUtil.isEmpty(list)) {
            list = new ArrayList<>();
        }
        return list;
    }

    @Nonnull
    @Override
    public List<CouponTemplateDO> getCouponTemplateList(List<Integer> canTakeTypes, Integer productScope,
                                                        Long productScopeValue, Integer count) {
        List<CouponTemplateDO> list = couponTemplateMapper.selectList(canTakeTypes, productScope, productScopeValue, count);
        if (CollUtil.isEmpty(list)) {
            list = new ArrayList<>();
        }
        return list;
    }

    @Nonnull
    @Override
    public List<CouponTemplateDO> getCouponTemplateList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return new ArrayList<>();
        }
        return couponTemplateMapper.selectByIds(ids);
    }

    @Nonnull
    @Override
    public List<CouponTemplateDO> getCanTakeCouponTemplateListByShopId(Long shopId) {
        List<CouponTemplateDO> list = couponTemplateMapper.selectCanTakeListByShopId(shopId);
        if (CollUtil.isEmpty(list)) {
            list = new ArrayList<>();
        }
        return list;
    }

    @Nonnull
    @Override
    public List<CouponTemplateDO> getCanTakeCouponTemplateListByShopIds(Set<Long> shopIds) {
        if (CollUtil.isEmpty(shopIds)) {
            return new ArrayList<>();
        }
        return couponTemplateMapper.selectCanTakeListByShopIds(shopIds);
    }

    @Override
    public int startCouponTemplate() {
        // 1. 查询待开始的优惠券模板
        List<CouponTemplateDO> list = couponTemplateMapper.selectList(new CoreLambdaQueryWrapper<CouponTemplateDO>()
                .eq(CouponTemplateDO::getStatus, CouponTemplateStatusEnum.WAIT.getStatus())
                .eq(CouponTemplateDO::getValidityType, CouponTemplateValidityTypeEnum.DATE)
                .ge(CouponTemplateDO::getValidStartTime, LocalDateTime.now()));
        if (CollUtil.isEmpty(list)) {
            return 0;
        }
        // 2. 遍历执行
        int count = 0;
        for (CouponTemplateDO couponTemplateDO : list) {
            try {
                if (startCouponTemplate(couponTemplateDO)) {
                    count++;
                }
            } catch (Exception e) {
                log.error("[startCouponTemplate][id({}) 更新为进行中出错]", couponTemplateDO.getId(), e);
            }
        }
        return count;
    }

    private boolean startCouponTemplate(CouponTemplateDO couponTemplateDO) {
        // 更新状态
        couponTemplateDO.setStatus(CouponTemplateStatusEnum.RUN.getStatus());
        int updateRows = couponTemplateMapper.update(couponTemplateDO, new CoreLambdaQueryWrapper<CouponTemplateDO>()
                .eq(CouponTemplateDO::getId, couponTemplateDO.getId())
                .eq(CouponTemplateDO::getStatus, CouponTemplateStatusEnum.WAIT.getStatus()));
        if (updateRows == 0) {
            log.error("[startCouponTemplate][id({}) 更新为进行中失败]", couponTemplateDO.getId());
            return false;
        }
        log.info("[startCouponTemplate][id({}) 更新为进行中成功]", couponTemplateDO.getId());
        return true;
    }

    @Override
    public int endCouponTemplate() {
        // 1. 查询进行中的优惠券模板
        List<CouponTemplateDO> list = couponTemplateMapper.selectList(new CoreLambdaQueryWrapper<CouponTemplateDO>()
                .eq(CouponTemplateDO::getStatus, CouponTemplateStatusEnum.RUN.getStatus())
                .eq(CouponTemplateDO::getValidityType, CouponTemplateValidityTypeEnum.DATE)
                .le(CouponTemplateDO::getValidEndTime, LocalDateTime.now()));
        if (CollUtil.isEmpty(list)) {
            return 0;
        }
        // 2. 遍历执行
        int count = 0;
        for (CouponTemplateDO couponTemplateDO : list) {
            try {
                if (endCouponTemplate(couponTemplateDO)) {
                    count++;
                }
            } catch (Exception e) {
                log.error("[endCouponTemplate][id({}) 更新为已结束出错]", couponTemplateDO.getId(), e);
            }
        }
        return count;
    }

    private boolean endCouponTemplate(CouponTemplateDO couponTemplateDO) {
        // 更新状态
        couponTemplateDO.setStatus(CouponTemplateStatusEnum.END.getStatus());
        int updateRows = couponTemplateMapper.update(couponTemplateDO, new CoreLambdaQueryWrapper<CouponTemplateDO>()
                .eq(CouponTemplateDO::getId, couponTemplateDO.getId())
                .eq(CouponTemplateDO::getStatus, CouponTemplateStatusEnum.RUN.getStatus()));
        if (updateRows == 0) {
            log.error("[endCouponTemplate][id({}) 更新为已结束失败]", couponTemplateDO.getId());
            return false;
        }
        log.info("[endCouponTemplate][id({}) 更新为已结束成功]", couponTemplateDO.getId());
        return true;
    }

}
